/* --------------------------------------------------------
 * Copyright (c) Express Scripts, Inc.  All rights reserved.
 * --------------------------------------------------------
 */
package com.aeky.common;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.Date;
import java.util.Map;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;

/**
 * The Enum FieldType.
 */
public enum FieldType {

    /** The STRING. */
    STRING {
        @Override
        public String getConditionString(Map<String, Object> params, String fieldName, Object value) {
            String fieldValue = value.toString().trim();
            if (fieldValue.matches(DATE_PATTERN)
                    && (fieldName.toLowerCase().contains("date") || (fieldName.toLowerCase().contains("time")))) {
                Date date = DateUtils.parseDate(fieldValue);
                return FieldType.DATE.getConditionString(params, fieldName, date);
            }
            if (!StringUtils.isBlank(fieldValue.toString())) {
                if ("\"null\"".equals(fieldValue)) {
                    return " and (" + fieldName + " is null or " + fieldName + "='')";
                } else if (fieldValue.startsWith("\"") && fieldValue.endsWith("\"") && fieldValue.length() > 1) {
                    fieldValue = fieldValue.substring(1, fieldValue.length() - 1);
                    return " and " + fieldName + "='" + StringEscapeUtils.escapeSql(fieldValue) + "'";
                } else if (fieldValue.startsWith("\'") && fieldValue.endsWith("\'") && fieldValue.length() > 1) {
                    fieldValue = fieldValue.substring(1, fieldValue.length() - 1);
                    return " and " + fieldName + "='" + StringEscapeUtils.escapeSql(fieldValue) + "'";
                } else if (fieldName.startsWith("$")) {
                    fieldName = fieldName.substring(1);
                    return " and "
                            + fieldName
                            + " like '"
                            + StringEscapeUtils.escapeSql(fieldValue.replaceAll("\\%", "\\\\%").replaceAll("\\_",
                                    "\\\\_")) + "%'";
                } else if (fieldName.startsWith("~")) {
                    fieldName = fieldName.substring(1);
                    return " and "
                            + fieldName
                            + " like '%"
                            + StringEscapeUtils.escapeSql(fieldValue.replaceAll("\\%", "\\\\%").replaceAll("\\_",
                                    "\\\\_")) + "'";
                } else {
                    return " and "
                            + fieldName
                            + " like '%"
                            + StringEscapeUtils.escapeSql(fieldValue.replaceAll("\\%", "\\\\%").replaceAll("\\_",
                                    "\\\\_")) + "%'";
                }
            }
            return "";
        }

        @Override
        public Criterion getConditionCriteria(String fieldName, Object value) {
            String fieldValue = value.toString().trim();
            if (fieldValue.matches(DATE_PATTERN)
                    && (fieldName.toLowerCase().contains("date") || (fieldName.toLowerCase().contains("time")))) {
                Date date = DateUtils.parseDate(fieldValue);
                return FieldType.DATE.getConditionCriteria(fieldName, date);
            }
            if ("\"null\"".equals(fieldValue)) {
                return Restrictions.or(Restrictions.isNull(fieldName), Restrictions.eq(fieldName, ""));
            } else if (fieldValue.startsWith("\"") && fieldValue.endsWith("\"") && fieldValue.length() > 1) {
                fieldValue = fieldValue.substring(1, fieldValue.length() - 1);
                return Restrictions.eq(fieldName, fieldValue);
            } else if (fieldValue.startsWith("\'") && fieldValue.endsWith("\'") && fieldValue.length() > 1) {
                fieldValue = fieldValue.substring(1, fieldValue.length() - 1);
                return Restrictions.eq(fieldName, fieldValue);
            } else {
                return Restrictions.like(fieldName, fieldValue.replaceAll("%", "\\%"), MatchMode.ANYWHERE);
            }
        }
    },
    NUMBER {
        @Override
        public String getConditionString(Map<String, Object> params, String fieldName, Object value) {
            BigDecimal fieldValue = new BigDecimal(value.toString());
            if (!StringUtils.isBlank(fieldValue.toString())) {
                if (fieldName.startsWith(">")) {
                    fieldName = fieldName.substring(1);
                    return " and " + fieldName + ">=" + fieldValue;
                } else if (fieldName.startsWith("<")) {
                    fieldName = fieldName.substring(1);
                    return " and " + fieldName + "<=" + fieldValue;
                } else {
                    return " and " + fieldName + "=" + fieldValue;
                }
            }
            return "";
        }

        @Override
        public Criterion getConditionCriteria(String fieldName, Object value) {
            BigDecimal fieldValue = new BigDecimal(value.toString());
            if (fieldName.startsWith(">")) {
                fieldName = fieldName.substring(1);
                return Restrictions.ge(fieldName, fieldValue);
            } else if (fieldName.startsWith("<")) {
                fieldName = fieldName.substring(1);
                return Restrictions.le(fieldName, fieldValue);
            } else {
                return Restrictions.eq(fieldName, fieldValue);
            }
        }
    },
    /** The ClinicalCheckEnum. */
    IsNullEnum {
        @Override
        public String getConditionString(Map<String, Object> params, String fieldName, Object fieldValue) {
            IsNullEnum status = (IsNullEnum) fieldValue;
            return status.getConditionString(params, fieldName);
        }

        @Override
        public Criterion getConditionCriteria(String fieldName, Object fieldValue) {
            IsNullEnum status = (IsNullEnum) fieldValue;
            return status.getCriterion(fieldName);
        }
    },
    /** The DATE. */
    DATE {
        @Override
        public String getConditionString(Map<String, Object> params, String fieldName, Object fieldValue) {
            if (fieldName.startsWith(">")) {
                fieldName = fieldName.substring(1);
                // LOG.debug("Date>=" + fieldValue.toString());
                String formatbgString = DateUtils
                        .formatDate(DateUtils.getDay((Date) fieldValue), "yyyy-MM-dd HH:mm:ss");

                // LOG.debug("formatbgString>=" + formatbgString);
                return " and " + fieldName + " >= '" + StringEscapeUtils.escapeSql(formatbgString) + "'";
            } else if (fieldName.startsWith("<")) {
                // LOG.debug("Date<" + fieldValue.toString());
                fieldName = fieldName.substring(1);
                String formatbgString = DateUtils.formatDate(DateUtils.addDate(DateUtils.getDay((Date) fieldValue), 1),
                        "yyyy-MM-dd HH:mm:ss");
                // LOG.debug("formatEndString<" + formatbgString);
                return " and " + fieldName + " < '" + StringEscapeUtils.escapeSql(formatbgString) + "'";
            } else {
                String formatEndString = DateUtils.formatDate(
                        DateUtils.addDate(DateUtils.getDay((Date) fieldValue), 1), "yyyy-MM-dd HH:mm:ss");
                String formatBeginString = DateUtils.formatDate(DateUtils.getDay((Date) fieldValue),
                        "yyyy-MM-dd HH:mm:ss");

                return " and " + fieldName + " >= '" + StringEscapeUtils.escapeSql(formatBeginString) + "' and "
                        + fieldName + " < '" + StringEscapeUtils.escapeSql(formatEndString) + "'";
            }

        }

        @Override
        public Criterion getConditionCriteria(String fieldName, Object fieldValue) {
            if (fieldName.startsWith(">")) {
                fieldName = fieldName.substring(1);
                return Restrictions.ge(fieldName, DateUtils.getDay((Date) fieldValue));
            } else if (fieldName.startsWith("<")) {
                fieldName = fieldName.substring(1);
                return Restrictions.le(fieldName, DateUtils.addDate(DateUtils.getDay((Date) fieldValue), 1));
            } else {
                return Restrictions.eq(fieldName, DateUtils.getDay((Date) fieldValue));
            }
        }
    },

    /** The COLLECTION. */
    COLLECTION {
        @Override
        public String getConditionString(Map<String, Object> params, String fieldName, Object fieldValue) {
            if (fieldName.startsWith("_")) {
                return " and " + fieldName.substring(1) + " in (" + fieldValue + ")";
            } else {
                return " and " + fieldName + " in (" + fieldValue + ")";
            }
        }

        @Override
        public Criterion getConditionCriteria(String fieldName, Object fieldValue) {
            return Restrictions.in(fieldName, (Collection<?>) fieldValue);
        }
    },

    /** The UNKNOW. */
    UNKNOW {
        @Override
        public String getConditionString(Map<String, Object> params, String fieldName, Object fieldValue) {
            return " and " + fieldName + "=" + fieldValue;
        }

        @Override
        public Criterion getConditionCriteria(String fieldName, Object fieldValue) {
            return Restrictions.eq(fieldName, fieldValue);
        }
    },
    /** The LONG. */
    LONG {
        @Override
        public String getConditionString(Map<String, Object> params, String fieldName, Object fieldValue) {
            return " and " + fieldName + "=" + fieldValue;
        }

        @Override
        public Criterion getConditionCriteria(String fieldName, Object fieldValue) {
            return Restrictions.eq(fieldName, fieldValue);
        }
    },

    /**
     * The BYTE .
     */
    BYTE {
        @Override
        public String getConditionString(Map<String, Object> params, String fieldName, Object fieldValue) {
            return " and " + fieldName + "=" + fieldValue;
        }

        @Override
        public Criterion getConditionCriteria(String fieldName, Object fieldValue) {
            return Restrictions.eq(fieldName, fieldValue);
        }
    };

    /**
     * Gets the field type.
     * 
     * @param fieldValue the field value
     * 
     * @return the field type
     */
    public static final FieldType getFieldType(Object fieldValue) {
        if (fieldValue instanceof String) {
            return STRING;
        } else if (fieldValue instanceof Date) {
            return DATE;
        } else if (fieldValue instanceof Long) {
            return LONG;
        } else if (fieldValue instanceof Byte) {
            return BYTE;
        } else if (fieldValue instanceof IsNullEnum) {
            return IsNullEnum;
        } else if (fieldValue instanceof Number) {
            return NUMBER;
        } else if (fieldValue instanceof Collection<?>) {
            return COLLECTION;
        } else {
            return UNKNOW;
        }
    }

    /**
     * Gets the condition string.
     * 
     * @param params the params
     * @param fieldName the field name
     * @param fieldValue the field value
     * 
     * @return the condition string
     */
    public abstract String getConditionString(Map<String, Object> params, String fieldName, Object fieldValue);

    /**
     * Gets the condition criteria.
     * 
     * @param fieldName the field name
     * @param fieldValue the field value
     * 
     * @return the condition criteria
     */
    public abstract Criterion getConditionCriteria(String fieldName, Object fieldValue);

    // private static final Logger LOG =
    // Logger.getLogger(GWTRPCServiceExporter.class);

    private static final String DATE_PATTERN = "(([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29)";
}
